Rebuild for plans-as-code (embedded TS evaluation) - #1
Merged
Conversation
Add the plans-as-code evaluation core (decisions 0011/0012/0014): rquickjs (QuickJS-ng) + oxc as the first external deps. Two-context bytecode dance (compile in Context::full, evaluate in a locked Context::custom over Json+Promise+Proxy), determinism allowlist (strip Math.random + transcendentals), bounded evaluation (time/memory/stack). A compass prelude served for the bare specifier provides plan/step/reconcile/evidence(atom+all/any/not via a vocabulary-free Proxy). Step identity is the declared export name, attached by a lazy host-injected footer and recovered after evaluation; revise/reconcile carry steps forward by identity. Root and revision modules evaluate and extract correctly; carry-forward proven. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reorient the whole tool around evaluation (decisions 0012/0014): - model: a Version is recovered by evaluating a module; identity is the sha256 of source bytes; keep validate (cycle/dup/dep), changes_nothing, filenames. - catalog: admission is byte-hash + filename check, no evaluation; parents are derived from import specifiers; source stored unchanged at 0444; evaluate() runs the module on demand. - chain: head/divergence/orphan now walk import-derived parents. - readiness: computed from an evaluated Version + events (per-head labelling moved to the CLI). - predicate: add decision-0013 self-contradiction detection (refused at commit) and the write-time evidence cross-check (reported, never refused). - CLI: start/commit/show/history/ready/status/verify/repair/progress/evidence/ version, each with --json parity and a convergence line; reads render not-found/unresolved/stopped distinctly. - refs: trimmed to event-id minting (plan/step identity is no longer minted). - drop change.rs (structural-diff rendering) and the KDL @block use for versions. Full e2e verified by hand: start -> edit -> commit -> show/history -> revise -> divergence -> reconcile -> converged, all steps carried forward. 129 lib tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…and VRS add-step defect - tests/acceptance.rs: examples evaluate + filenames reproduce from source bytes; reconciliation carries both sides; full e2e lifecycle through the CLI (start -> commit -> show/history/ready -> revise -> diverge -> reconcile); and engine failure-mode guards (unresolved / stopped / sandbox: no eval, Date, Math.random). - eval.rs: a missing imported predecessor now reads as Unresolved (wait), not a generic Failed — resolution succeeds so the loader reports the absent file. Drop the unused ctx threading through extract/to_pred. - cmd.rs: the changes-nothing refusal is a plain string, not format!. - context/06-api/spec.md: the 'A revision' example added a Step anonymously, which has no identity (decision 0012) — same defect the example plans had. Added steps are now named exports, so spec, prelude, and examples agree. - example READMEs: point at the corrected version filenames. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- examples/README.md: the examples are evaluated (they are the acceptance suite), no longer 'not yet evaluated'. - 02-artifacts DELTA-001: a distinct `repair` command now exists (refuses on a clean catalog, names the last intact predecessor); narrowed from open, with the remaining gap (author vs scaffold) recorded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The acceptance suite reads the committed example plans as fixtures, but the fileset excluded examples/, so the tests passed locally (examples present in the worktree) and failed under nix build (examples stripped from the sandbox source). Only nix build catches this. Adding examples/ to the fileset.
A nix build output symlink whose blob content is a /nix/store path — not something to carry in a public repo, and it churns on every build. Untrack it and gitignore it.
schickling
marked this pull request as ready for review
July 27, 2026 08:06
Contributor
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e824356c91
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…dules The ESM resolver accepted absolute and outside-the-catalog relative paths, read them via SourceLoader, and evaluated them — handing a plan a filesystem -read capability and breaking determinism, in violation of the capability boundary (0011) and locked-evaluation intent (0014). The resolver now permits only the bare `compass` prelude and content-addressed plan-version modules: a same-plan sibling `./<seq>-<hash>.ts` or a cross-plan `../../<plan>/versions/<seq>-<hash>.ts`. Paths are normalised lexically (never against the real filesystem), so no `..` or symlink can redirect resolution outside the catalog. Absolute paths, escaping references, and non-content -addressed names are refused with a clear "illegitimate import" error. The loader additionally enforces admission (content hash matches filename, per 02-artifacts) on every imported version, while sparing the entry module being authored. Shared classification helpers (`normalize_lexical`, `import_target_plan`) are exposed for the predecessor logic to reuse. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Commit resolved every imported version-file against the current plan's store and required each to be a committed predecessor, so a cross-plan reference (`../../pl_other/versions/...`) failed to resolve and the module was wrongly rejected as having an uncommitted predecessor — leaving the documented cross-plan Step reference (CMP.API-R05) unusable. An import is now a predecessor only when it is a version of the same plan; an import of another plan's version is a cross-plan reference, resolved against that other plan's store and required to be admitted there, but never made a parent of the current version. Catalog admission applies the same rule when deriving lineage, so a cross-plan reference no longer appears as an orphan predecessor edge. Both paths classify through the shared import_target_plan. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When two reconciliation sides defined the same named Step with different content, reconcile silently kept the first side and dropped the other — the silent intent loss the whole reconciliation design exists to prevent. Reconcile now detects when sides disagree on a same-identity Step (comparing work, dependencies, acceptance, supersedes, and retirement) and refuses the reconciliation unless it carries an explicit `edit` for that Step stating the surviving intent. The error names the Step and both differing sides. A Step only one side carries, or that both sides left identical, is not a conflict and is carried forward unchanged. Resolves DQ08 in favour of never asserting intent nobody wrote. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- imports_are_restricted_to_legitimate_modules: an absolute or catalog-escaping import is refused at eval; a valid same-plan sibling resolves. - a_cross_plan_reference_is_not_a_predecessor: a cross-plan reference commits successfully, records no parent, and does not read as an orphan. - reconciliation_refuses_divergent_same_step_without_an_explicit_edit: a divergent same-step reconcile is refused naming the step; an explicit edit resolves it; a non-conflicting reconcile still works. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- decision 0016: a divergent same-Step reconciliation edit must be resolved explicitly (resolves DQ08), matching the never-silently-lose-intent principle. - 06-api: the documented cross-plan dependsOn edge is not usable — validate() checks deps against the version's own steps and readiness folds within one plan. Narrowed CMP.API-R05 to the reference (which works), deferred the dependency edge to DQ11, and fixed the import path depth (../../, not ../). - 05-integrations: refined CMP.INT-R06 — the substrate cannot express completeness any more than the catalog can, so convergence is at most 'quiescent as of now', never 'you have everything'. - DQ12: variable-in-content vs relative-import+env-root machine-agnosticism. - fix the repair delta to the enforced shape (status open, bare direction).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebuilds Compass for the plans-as-code design settled in
context/(decisions 0011–0015): intent is authored as pure TypeScript, a version is the authored module stored unchanged and named by the hash of its source bytes, and reading a plan evaluates it in an embedded, capability-free engine.What changed
src/eval.rs): rquickjs (QuickJS-ng) + oxc, the crate's first external deps. Two-context bytecode dance — compile in a full context, evaluate in a locked one — soeval/new Functionexist but are inert. Determinism allowlist (Math.random + transcendentals removed; no clock/fs/net/Intl). Bounded in time, memory, and stack.compassprelude: the library plans import —plan/step/revise/reconcile/evidence(atom + all/any/not, vocabulary-free). A step's identity is its declared export name, recovered after evaluation and carried forward by revision.start · commit · show · history · ready · status · verify · repair · progress · evidence · version.add: [step({...})]was a real defect in both the examples and06-api/spec.md, found by building the thing. Fixing it changed the example bytes, hence new content-hash filenames.Verification
cargo build --release,cargo clippy --all-targets -- -D warnings,cargo fmt --check— cleancargo test— 129 lib + 7 acceptancenix build .#compass— passesNot in this PR
context/VRS is unchanged except the06-apiexample fix; the design was settled separately onmain.Draft for review of the KDL→TS cutover before it lands on
main.